package com.dearbinge.utils.httpClient;


import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.config.*;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import java.io.IOException;
import java.nio.charset.CodingErrorAction;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;

/**
 * @author 90
 */
final class RequestSinker {

    private final static Logger log = LoggerFactory.getLogger(RequestSinker.class);

    private static CloseableHttpClient HTTP_CLIENT;
    private static PoolingHttpClientConnectionManager CONNECTION_MANAGER;

    static {

        CONNECTION_MANAGER = new PoolingHttpClientConnectionManager();
        SocketConfig socketConfig = SocketConfig.custom().setTcpNoDelay(true).build();

        MessageConstraints messageConstraints = MessageConstraints.custom().setMaxHeaderCount(200).setMaxLineLength(2000).build();
        ConnectionConfig connectionConfig = ConnectionConfig.custom().setMalformedInputAction(CodingErrorAction.IGNORE).setUnmappableInputAction(CodingErrorAction.IGNORE).setCharset(Consts.UTF_8).setMessageConstraints(messageConstraints).build();

        RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(120000).setConnectTimeout(10000).setConnectionRequestTimeout(120000)
                .setStaleConnectionCheckEnabled(true).build();

        SSLContext sslContext;
        try {

            sslContext = createIgnoreVerifySSL();

            SSLConnectionSocketFactory ssff = new SSLConnectionSocketFactory(sslContext, SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
            Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create().register("http", PlainConnectionSocketFactory.INSTANCE).register("https", ssff).build();
            CONNECTION_MANAGER = new PoolingHttpClientConnectionManager(socketFactoryRegistry);

            CONNECTION_MANAGER.setMaxTotal(100);
            CONNECTION_MANAGER.setDefaultMaxPerRoute(50);
            CONNECTION_MANAGER.setDefaultSocketConfig(socketConfig);
            CONNECTION_MANAGER.setDefaultConnectionConfig(connectionConfig);

            HTTP_CLIENT = HttpClients.custom().setConnectionManager(CONNECTION_MANAGER).setDefaultRequestConfig(requestConfig).build();
            //HTTP_CLIENT = HttpClients.custom().setConnectionManager(CONNECTION_MANAGER).setSSLSocketFactory(sslsf).setDefaultRequestConfig(requestConfig)
            //		.build();
        } catch (Exception e) {
            log.error("[连接异常]", e.getMessage());
            //			e.printStackTrace();
        }

    }

    public static SSLContext createIgnoreVerifySSL() throws NoSuchAlgorithmException, KeyManagementException {
        SSLContext sc = SSLContext.getInstance("TLS");

        // 实现一个X509TrustManager接口，用于绕过验证，不用修改里面的方法
        X509TrustManager trustManager = new X509TrustManager() {
            @Override
            public void checkClientTrusted(java.security.cert.X509Certificate[] paramArrayOfX509Certificate, String paramString) throws CertificateException {
            }

            @Override
            public void checkServerTrusted(java.security.cert.X509Certificate[] paramArrayOfX509Certificate, String paramString) throws CertificateException {
            }

            @Override
            public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                return null;
            }
        };

        sc.init(null, new TrustManager[]{trustManager}, null);
        return sc;
    }

    /**
     * @param request
     * @return
     */
    static ApiResponse sink(ApiRequest request) {
        HttpUriRequest httpRequest = UriRequest(request);
        return RequestSinker.doSink(httpRequest, request);
    }

    private static ApiResponse doSink(HttpUriRequest httpRequest, ApiRequest request) {
        HttpEntity entity = null;
        CloseableHttpResponse httpResponse = null;
        try {
            HttpClientContext context = HttpClientContext.create();
            httpResponse = HTTP_CLIENT.execute(httpRequest, context);
            entity = httpResponse.getEntity();
            if (null == entity) {
                return new ApiResponse(httpResponse, context, new byte[0]);
            }
            byte[] content = EntityUtils.toByteArray(entity);
            ApiResponse response = new ApiResponse(httpResponse, context, content);
            return response;
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            responseClose(httpResponse);
            EntityUtils.consumeQuietly(entity);
        }
    }

    private static HttpUriRequest UriRequest(ApiRequest request) {
        HttpUriRequest httpUriRequest = null;
        switch (request.getVerb()) {
            case GET:
                httpUriRequest = ApiHttpRequestUrlBuilder.buildHttpGet(request);
                break;
            case POST:
                HttpPost httpPost = ApiHttpRequestUrlBuilder.buildHttpPost(request);
                httpUriRequest = httpPost;
                break;

            default:
                throw new UnsupportedOperationException("Only supported GET or POST verb.");
        }

        for (String key : request.getHeaders().keySet()) {
            httpUriRequest.setHeader(key, request.getHeaders().get(key));
        }

        return httpUriRequest;
    }

    private static void responseClose(CloseableHttpResponse httpResponse) {
        if (httpResponse != null) {
            try {
                httpResponse.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }
}
